﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using Yuchen.NPA.Core.CompilerService.RegularExpression;

namespace Yuchen.NPA.Core.CompilerService.NQL
{
    public class Lexer
    {
        private static readonly string[] keyWords = new string[]
        {
            "\\*",
            "+",
            "-",
            "/",
            "\\*=",
            "+=",
            "-=",
            "/=",
            "=",
            "%",
            "%=",
            ">=",
            "<=",
            ">",
            "!",
            ".",
            //"\\$",
            "\\w*",
            "[1-9][0-9]*",
            "\\(",
            "\\)",
            @"'[\w!@#$%^&\(\)-_=+{}\|:""<>?\[\]\\;,./]*'"
        };

        private static readonly string[] backquote = new[] { "`" };

        private static readonly string[] dot = new[] { "." };

        private static readonly string _regex = "";

        private static readonly IDictionary<RegularExpression.State, IDictionary<char, RegularExpression.State>> _map = new Dictionary<RegularExpression.State, IDictionary<char, RegularExpression.State>>();
        static Lexer()
        {
            _regex = string.Join("|", keyWords.Union(backquote).Union(dot));
#if !DEBUG
            try
            {
#endif

            if (File.Exists(@"D:\dfa.bin"))
            {
                var file = File.OpenRead(@"D:\dfa.bin");
                using (file)
                {
                    _map =
                        (IDictionary<RegularExpression.State, IDictionary<char, RegularExpression.State>>)
                        new BinaryFormatter().Deserialize(file);
                }
            }
            else
            {
                var parser = new RegexParser(_regex);
                var state = parser.ToMinimizeDFA();
                state.States.ForEach(s =>
                {
                    _map.Add(s, s.NextStates.ToDictionary(x => x.Key.InputCharacter.Value, x => x.Value));
                });
                var file = File.OpenWrite(@"D:\dfa.bin");
                using (file)
                {
                    new BinaryFormatter().Serialize(file, _map);
                    file.Flush();
                }
            }
#if !DEBUG
        }
            catch (Exception)
            {

            }
#endif

        }
        private static readonly char[] keyWordsFirstCharLowers = keyWords.Select(x => x[0]).ToArray();
        private int currentIndex = 0;
        private int line = 0;
        private int column = 0;
        private readonly List<char> chars;
        public Lexer(string text)
        {
            chars = text.ToList();
        }

        private int Peek(int i)
        {
            if (chars.Count < i + 1)
            {
                return -1;
            }

            return chars[i];
        }

        public Token Read()
        {
            if (currentIndex >= chars.Count)
            {
                return null;
            }
            var token = new Token();
            token.Span.Start = currentIndex;
            var state = _map.FirstOrDefault().Key;
            var resultChars = new Stack<char>();
            var stateStack = new Stack<RegularExpression.State>();
            while (!state.IsError)
            {
                var c = Peek(currentIndex++);
                if (c == -1)
                {
                    break;
                }

                if (c.ToString() == Environment.NewLine)
                {
                    line++;
                    column = 0;
                }
                else
                {
                    column++;
                }

                var currentChar = (char)c;
                resultChars.Push(currentChar);
                if (_map[state].TryGetValue(currentChar, out state))
                {
                    if (state.IsAccepted)
                    {
                        stateStack.Clear();
                    }

                    stateStack.Push(state);
                }
                else
                {
                    state = new RegularExpression.State()
                    {
                        IsError = true
                    };
                }
            }

            while (!state.IsAccepted && stateStack.Any())
            {
                state = stateStack.Pop();
                resultChars.Pop();
                currentIndex--;
            }

            if (!state.IsAccepted)
            {
                throw new Exception("未能成功读取");
            }

            while (true)
            {
                var c = Peek(currentIndex);
                if (c == -1)
                {
                    break;
                }

                if ((char)c == ' ')
                {
                    currentIndex++;
                    continue;
                }
                break;
            }
            token.Text = new string(resultChars.Reverse().ToArray());
            token.Span.Length = token.Text.Length;
            token.Span.End = token.Span.Start + token.Span.Length;

            return token;
        }
    }
}
